package com.apes.fn.scm.internal.help.service.voucher.retail;

import com.alibaba.fastjson.JSON;
import com.apes.fn.scm.retailOrder.model.RetailOrder;
import com.apes.fn.scm.retailReturn.RetailReturn;
import com.apes.fn.scm.retailReturn.RetailReturnItem;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.outbox.OutboxEventService;
import com.apes.framework.plugin.outbox.OutboxPayloadWrapper;
import com.apes.framework.util.LogicalUtil;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.ReflectUtil;
import com.apes.scm.account.account.model.InventoryBatch;
import com.apes.scm.account.account.service.InventoryService;
import com.apes.scm.account.account.service.ProductAccountService;
import com.apes.scm.contract.model.Contract;
import com.apes.scm.contract.service.ContractService;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.frame.model.DeptEnum;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.voucher.model.Voucher;
import com.apes.scm.voucher.model.VoucherInventory;
import com.apes.scm.voucher.model.VoucherItem;
import com.apes.scm.voucher.repository.VoucherInventoryRepository;
import com.apes.scm.voucher.repository.VoucherRepository;
import com.apes.scm.voucher.service.BusinessType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 代销带安退货
 *
 * @Author wuq
 * @Date 2021-10-19
 */
@Service
public class RetailOutHelpReturnVoucherService extends DomainService {

    @Autowired
    private VoucherInventoryRepository voucherInventoryRepository;
    @Autowired
    private VoucherRepository voucherRepository;
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private ContractService contractService;
    @Autowired
    private ProductAccountService productAccountService;


    public void process(RetailReturn retailReturn) {

        // 过滤出是代销带安的部分
        List<RetailReturnItem> retailReturnItems = retailReturn.getSaleItems().stream()
                .filter(item -> LogicalUtil.in(item.getRetailOrderItem().getProductRule(), 10))
                .collect(Collectors.toList());

        Map<Contract, List<RetailReturnItem>> outRetailOrderItems = retailReturnItems.stream()
                .collect(Collectors.groupingBy(retailReturnItem -> retailReturnItem.getRetailOrderItem().getContract()));

        outRetailOrderItems.forEach((contract, returnItems) -> {
            // 判断合同公司与工单上面的公司是否一致
            if ("supplier".equals(contract.getContractType().getCode()) && !Objects.equals(contract.getCompany().getId(), retailReturn.getCompany().getId())) {
                // 退跨公司采购
                returnCrossPurchaseVoucher(retailReturn, returnItems, contract);
            } else {
                // 直接退供应商采购
                returnPurchaseVoucher(retailReturn, returnItems, contract);
            }

            // 集群空间额外逻辑计算
            extracted(retailReturn, contract, returnItems);
        });
    }

    private void extracted(RetailReturn retailReturn, Contract contract, List<RetailReturnItem> returnItems) {
        //冲红外协派工集群空间类型生成供应商收入单和平台费用单
        OutboxPayloadWrapper wrapper = new OutboxPayloadWrapper();
        wrapper.setSubscriber("local.SupplierPlatformFnService.createReturnIncomeAndExpense");
        Map msg = MapUtil.mapper(
                "type","retailOutHelpReturn",
                "contract",MapUtil.mapped(contract),
                "retailReturnItems",MapUtil.mapped(returnItems)
        );
        wrapper.setPayload(JSON.toJSONString(msg));
        wrapper.setAggregateId(retailReturn.getId());
        SpringManager.getBean(OutboxEventService.class).sendSaveEvent(wrapper);
    }

    private void returnPurchaseVoucher(RetailReturn retailReturn, List<RetailReturnItem> retailReturnItems, Contract contract) {
        // 找原销售凭证
        Voucher originSaleVoucher = voucherRepository.findOne(
                        JpaDsl.toCriteriaByEq("sourceBusinessType", RetailReturn.class.getSimpleName(),
                                "source", retailReturn.getId(), "origin", retailReturn.getId(), "voucherType", "CUSTOMER_SALE"))
                .orElseThrow(() -> new RuntimeException("生成代销带安返厂凭证时，未找到：" + retailReturn.getSaleOrder().getId() + " 的业务凭证！"));
        returnOutPurchaseVoucher(contract, contract.getPartner(), originSaleVoucher, retailReturn, retailReturnItems, true);   // 直接退供应商采购
    }

    private void returnCrossPurchaseVoucher(RetailReturn retailReturn, List<RetailReturnItem> retailReturnItems, Contract contract) {
        PartyRole company = contract.getCompany().getParty().getPartyRoleByType("Company");         // 合同上面的公司
        PartyRole partner = retailReturn.getCompany().getParty().getPartyRoleByType("Customer");    // 门店公司

        // 第一步：门店对合同公司做返厂
        Voucher storeOriginSaleVoucher = voucherRepository.findOne(
                        JpaDsl.toCriteriaByEq("sourceBusinessType", RetailOrder.class.getSimpleName(),
                                "source", retailReturn.getSaleOrder().getId(), "origin", retailReturn.getSaleOrder().getId(), "voucherType", "CUSTOMER_SALE"))
                .orElseThrow(() -> new RuntimeException("生成代销带安返厂凭证时，未找到：" + retailReturn.getSaleOrder().getId() + " 的业务凭证！"));
        // 获取合同（内部修改为经销合同）
        Contract innerContract = contractService.getContract(retailReturn.getDept(), company.getParty().getPatyRoleByType("Supplier"), "distribution", "supplier", "零售代销带安退货");
        returnOutPurchaseVoucher(innerContract, company, storeOriginSaleVoucher, retailReturn, retailReturnItems, true);

        // 第二步：合同公司退对门店的销售
        // 找原内部销售凭证
        String originNumber = retailReturnItems.get(0).getRetailOrderItem().getId();                          // 由于外协部分是在一张凭证上面，这里就取其中的一条
        Voucher originSaleVoucher = voucherRepository.findSaleVoucherByCompanyAndItemOrigin(company, partner, originNumber);
        if (originSaleVoucher == null) throw new RuntimeException("没有找到对应的销售凭证，请检查！");
        returnSaleVoucher(retailReturn, retailReturnItems, originSaleVoucher);

        // 第三步：退对供应商的采购
        returnSupplierPurchaseVoucher(contract, contract.getPartner(), retailReturn, retailReturnItems, false);
    }

    private void returnOutPurchaseVoucher(Contract contract, PartyRole partner, Voucher originSaleVoucher,
                                          RetailReturn retailReturn, List<RetailReturnItem> retailReturnItems, final boolean stockFlag) {
        Voucher voucher = new Voucher();
        voucher.setReturned(true);
        voucher.setBusinessType(BusinessType.PURCHASE_RETURN);

        voucher.setPartner(partner.getParty().getPartyRoleByType("Supplier"));
        voucher.setCompany(retailReturn.getCompany());
        voucher.setDept(retailReturn.getDept());
        voucher.setClerk(retailReturn.getClerk());

        voucher.setContract(contract);
        voucher.setInventoryMode(contract.getCooperation().getInventoryMode());
        voucher.setAccountingMethod(contract.getCooperation().getAccountingMethod());

        voucher.setScene(retailReturn.getScene());
        voucher.setOrigin(retailReturn.getId());
        voucher.setOriginBusinessType(retailReturn.getInnerBusinessType());
        voucher.setSource(retailReturn.getId());
        voucher.setSourceBusinessType(retailReturn.getInnerBusinessType());
        voucher.setCustomerExpand(retailReturn.getCustomerExpand().getId());

        voucher.setVoucherItems(
                retailReturnItems.stream()
                        .map(retailReturnItem -> {
                            VoucherItem voucherItem = new VoucherItem();

                            voucherItem.setProduct(retailReturnItem.getProduct());
                            voucherItem.setUom(retailReturnItem.getUom());
                            voucherItem.setProductQty(-1.00 * retailReturnItem.getProductUomQty());
                            voucherItem.setOriginItemId(String.valueOf(retailReturnItem.getId()));
                            voucherItem.setSourceItemId(String.valueOf(retailReturnItem.getId()));

                            //找到原销售凭证的明细行
                            VoucherItem saleVoucherItem = originSaleVoucher.getVoucherItems().stream()
                                    .filter(saleItem -> saleItem.getProduct().getId().equals(retailReturnItem.getProduct().getId()))
                                    .findFirst()
                                    .orElseThrow(() -> new RuntimeException("生成返厂凭证时，根据商品编码没有找到对应的凭证明细，" +
                                            "商品编码：" + retailReturnItem.getProduct().getId() + "，凭证编码：" + originSaleVoucher.getId()));

                            // 获取原销售批次
                            VoucherInventory voucherInventory = voucherInventoryRepository.findAll(
                                            JpaDsl.toCriteriaByEq("voucher.id", originSaleVoucher.getId(), "voucherItem", saleVoucherItem))
                                    .stream().findFirst().get();

                            InventoryBatch inventoryBatch = voucherInventory.getInventoryBatch();
                            voucherItem.setInventoryBatch(inventoryBatch);
                            voucherItem.setPriceUnit(inventoryBatch.getTaxCostPrice());
                            voucherItem.setSupplier(inventoryBatch.getSupplier());
                            voucherItem.setTax(inventoryBatch.getTax());
                            voucherItem.setOwner(retailReturn.getCompany());

                            //  增加（减少）冻结数量
                            inventoryService.changeFrostQty(inventoryBatch, retailReturnItem.getUom(), retailReturnItem.getProductUomQty(), inventoryBatch.getCompany(),
                                    retailReturn.getInnerBusinessType(), retailReturn.getId(), retailReturnItem.getId(), "生成零售外协商品返厂凭证");

                            // 门店退回时，判断是否需要减少库存（由于实物商品在做零售退回时，增加了库存，这里需要还原）
                            if (stockFlag && "product".equals(retailReturnItem.getProduct().getType())) {
                                extracted(retailReturnItem, retailReturn, voucher);
                            }

                            return voucherItem;
                        }).collect(Collectors.toSet()));

        Voucher newVoucher = invoke("voucher.create", MapUtil.mapped(voucher));
        if ("IM_OWN".equals(newVoucher.getInventoryMode()) && newVoucher.getAmtTotal() != 0) {
            createFixedOriginNumberInvoice(retailReturn, newVoucher);
        }
    }

    /**
     * 构建销售退回凭证
     *
     * @param retailReturn      零售退货主表
     * @param retailReturnItems 零售退货明细
     * @param originSaleVoucher 原销售凭证
     */
    private void returnSaleVoucher(RetailReturn retailReturn, List<RetailReturnItem> retailReturnItems, Voucher originSaleVoucher) {
        Voucher voucher = new Voucher();
        // 销售退回必填字段
        voucher.setReturned(true);
        voucher.setBusinessType(BusinessType.SALE_RETURN);
        voucher.setVoucher(originSaleVoucher);

        ReflectUtil.copy(originSaleVoucher, voucher, "dept", "company", "partner", "contract");
        voucher.setInventoryMode(originSaleVoucher.getInventoryMode());     //设置库存模式
        voucher.setAccountingMethod(originSaleVoucher.getAccountingMethod());  // 核算方式

        voucher.setCustomerExpand(retailReturn.getCustomerExpand().getId());
        voucher.setScene(retailReturn.getScene());
        voucher.setOrigin(retailReturn.getId());
        voucher.setOriginBusinessType(retailReturn.getInnerBusinessType());
        voucher.setSource(retailReturn.getId());
        voucher.setSourceBusinessType(retailReturn.getInnerBusinessType());

        voucher.setVoucherItems(
                originSaleVoucher.getVoucherItems().stream().map(saleItem -> {
                    VoucherItem voucherItem = new VoucherItem();

                    retailReturnItems.stream()
                            .filter(returnItem -> returnItem.getProduct().getId().equals(saleItem.getProduct().getId()))
                            .forEach(retailReturnItem -> {
                                voucherItem.setOwner(originSaleVoucher.getCompany());
                                voucherItem.setProduct(saleItem.getProduct());
                                voucherItem.setTax(saleItem.getTax());

                                voucherItem.setUom(saleItem.getUom());          // 设置为销售的单位以及数量
                                voucherItem.setProductQty(-1 * Math.abs(saleItem.getProductQty()));
                                voucherItem.setPriceUnit(saleItem.getPriceUnit());      // 默认为退货单价

                                voucherItem.setOriginItemId(retailReturnItem.getId());
                                voucherItem.setSourceItemId(retailReturnItem.getId());
                                voucherItem.setVoucherItem(saleItem);   // 退回时传入明细行号
                            });

                    return voucherItem;
                }).filter(voucherItem -> voucherItem.getProduct() != null).collect(Collectors.toSet()));

        voucher = invoke("voucher.create", MapUtil.mapped(voucher));
        if ("IM_OWN".equals(voucher.getInventoryMode()) && voucher.getAmtTotal() != 0) {
            createFixedOriginNumberInvoice(retailReturn, voucher);
        }
    }

    /**
     * 退外部供应商
     * 跨公司外协时，找供应商采购时，修改为代销模式
     *
     * @param contract          合同
     * @param partner           合作伙伴
     * @param retailReturn      零售退货单
     * @param retailReturnItems 零售退货明细
     * @param stockFlag         库存扣减标记
     */
    private void returnSupplierPurchaseVoucher(Contract contract, PartyRole partner,
                                               RetailReturn retailReturn, List<RetailReturnItem> retailReturnItems,
                                               final boolean stockFlag) {
        Voucher voucher = new Voucher();
        voucher.setReturned(true);
        voucher.setBusinessType(BusinessType.PURCHASE_RETURN);

        voucher.setPartner(partner.getParty().getPartyRoleByType("Supplier"));
        voucher.setCompany(contract.getCompany());
        voucher.setDept(contract.getDept());
        voucher.setClerk(retailReturn.getClerk());

        voucher.setContract(contract);
        voucher.setInventoryMode(contract.getCooperation().getInventoryMode());
        voucher.setAccountingMethod(contract.getCooperation().getAccountingMethod());

        voucher.setScene(retailReturn.getScene());
        voucher.setOrigin(retailReturn.getId());
        voucher.setOriginBusinessType(retailReturn.getInnerBusinessType());
        voucher.setSource(retailReturn.getId());
        voucher.setSourceBusinessType(retailReturn.getInnerBusinessType());
        voucher.setCustomerExpand(retailReturn.getCustomerExpand().getId());

        Set<VoucherItem> voucherItems = new HashSet(); //凭证明细
        // 明细处理
        for (int i = 0, len = retailReturnItems.size(); i < len; i++) {
            RetailReturnItem retailReturnItem = retailReturnItems.get(i);

            Dept dept = voucher.getDept();
            if (dept == null) {
                throw new RuntimeException("业务凭证销售部门为空！");
            }
            if (!(dept.existDeptType(DeptEnum.RETAIL) || dept.existDeptType(DeptEnum.SALE))) {
                throw new RuntimeException("业务凭证部门为非销售部门！");
            }
            if (!dept.existDeptType(DeptEnum.PURCHASE)) {
                throw new RuntimeException("业务凭证部门为非采购部门！");
            }

            // 查找对应的批次数据
            List<InventoryBatch> inventoryBatches = inventoryService.findInventoryBatchForSaleOrder(voucher.getCompany(), dept, voucher.getPartner(), retailReturnItem.getProduct());
            if (inventoryBatches.isEmpty()) throw new RuntimeException("代销带安退货时，没有找到对应的批次数据");
            double countBatchNumber = retailReturnItem.getProductUomQty();   // 计数器
            // 循环与批次数据作比较，根据批次拆分
            for (InventoryBatch inventoryBatch : inventoryBatches) {

                if (inventoryBatch.getBalanceQty() >= countBatchNumber) {    // 批次库存大于退货数量
                    VoucherItem voucherItem = new VoucherItem();
                    voucherItem.setOwner(contract.getCompany());
                    voucherItem.setProduct(retailReturnItem.getProduct());

                    voucherItem.setUom(retailReturnItem.getUom());
                    voucherItem.setProductQty(-1.00 * countBatchNumber);
                    voucherItem.setOriginItemId(String.valueOf(retailReturnItem.getId()));
                    voucherItem.setSourceItemId(String.valueOf(retailReturnItem.getId()));
                    voucherItem.setInventoryBatch(inventoryBatch);
                    voucherItem.setPriceUnit(inventoryBatch.getTaxCostPrice());
                    voucherItem.setSupplier(inventoryBatch.getSupplier());
                    voucherItem.setTax(inventoryBatch.getTax());

                    //  增加（减少）冻结数量
                    inventoryService.changeFrostQty(inventoryBatch, retailReturnItem.getUom(), retailReturnItem.getProductUomQty(), inventoryBatch.getCompany(),
                            retailReturn.getInnerBusinessType(), retailReturn.getId(), retailReturnItem.getId(), "生成跨公司零售外协商品返厂凭证");

                    // 门店退回时，判断是否需要减少库存（由于实物商品在做零售退回时，增加了库存，这里需要还原）
                    if (stockFlag && "product".equals(retailReturnItem.getProduct().getType()) ) {
                        extracted(retailReturnItem, retailReturn, voucher);
                    }

                    voucherItems.add(voucherItem);
                    break;
                } else {    // 批次库存小于退货数量时
                    VoucherItem voucherItem = new VoucherItem();
                    voucherItem.setOwner(contract.getCompany());
                    voucherItem.setProduct(retailReturnItem.getProduct());

                    voucherItem.setUom(retailReturnItem.getUom());
                    voucherItem.setProductQty(-1.00 * inventoryBatch.getBalanceQty());
                    voucherItem.setOriginItemId(String.valueOf(retailReturnItem.getId()));
                    voucherItem.setSourceItemId(String.valueOf(retailReturnItem.getId()));
                    voucherItem.setInventoryBatch(inventoryBatch);
                    voucherItem.setPriceUnit(inventoryBatch.getTaxCostPrice());
                    voucherItem.setSupplier(inventoryBatch.getSupplier());
                    voucherItem.setTax(inventoryBatch.getTax());

                    //  增加（减少）冻结数量
                    inventoryService.changeFrostQty(inventoryBatch, retailReturnItem.getUom(), retailReturnItem.getProductUomQty(), inventoryBatch.getCompany(),
                            retailReturn.getInnerBusinessType(), retailReturn.getId(), retailReturnItem.getId(), "生成跨公司零售外协商品返厂凭证");

                    // 门店退回时，判断是否需要减少库存（由于实物商品在做零售退回时，增加了库存，这里需要还原）
                    if (stockFlag && "product".equals(retailReturnItem.getProduct().getType()) ) {
                        //写商品库存帐可用数
                        extracted(retailReturnItem, retailReturn, voucher);
                    }

                    countBatchNumber -= inventoryBatch.getBalanceQty();     // 计数操作
                    voucherItems.add(voucherItem);
                }
            }
        }
        voucher.setVoucherItems(voucherItems);

        Voucher newVoucher = invoke("voucher.create", MapUtil.mapped(voucher));
        if ("IM_OWN".equals(newVoucher.getInventoryMode()) && newVoucher.getAmtTotal() != 0) {
            createFixedOriginNumberInvoice(retailReturn, newVoucher);
        }
    }

    /**
     * 扣减库存
     *
     * @param retailReturnItem RetailReturnItem
     * @param retailReturn     RetailReturn
     * @param voucher          Voucher
     */
    private void extracted(RetailReturnItem retailReturnItem, RetailReturn retailReturn, Voucher voucher) {
        //写商品库存帐可用数
        productAccountService.setProductAvailable(retailReturnItem.getProduct(), retailReturnItem.getLocation(), retailReturnItem.getUom(),
                -1 * Math.abs(retailReturnItem.getProductUomQty()), retailReturn.getInnerBusinessType(), retailReturn.getId(),
                retailReturnItem.getId(), "零售退货退工单外协部分", voucher.getCompany());

        //写商品库存帐保管数
        productAccountService.setProductCustody(retailReturnItem.getProduct(), retailReturnItem.getLocation(), retailReturnItem.getUom(),
                -1 * Math.abs(retailReturnItem.getProductUomQty()), retailReturn.getInnerBusinessType(), retailReturn.getId(),
                retailReturnItem.getId(), "零售退货退工单外协部分", voucher.getCompany());
    }

    private void createFixedOriginNumberInvoice(RetailReturn retailReturn, Voucher voucher) {
        // 获取 invoice Mapping 文件
        Map m = invoke("voucher.flow.invoice", MapUtil.mapped(voucher));

        m.put("origin", retailReturn.getSaleOrder().getSource());
        m.put("originBusinessType", retailReturn.getSaleOrder().getSourceBusinessType());

        // 生成账单
        invoke("invoice.create", m);
    }
}
